// File: jmcomic-core/src/main/java/dev/jukomu/jmcomic/util/JmCryptoUtils.java
package dev.jukomu.jmcomic.util;

import dev.jukomu.common.util.Asserts;
import dev.jukomu.jmcomic.config.JmMagicConstants;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;

/**
 * 对应Python的JmCryptoTool，提供禁漫加解密相关逻辑。
 */
public class JmCryptoUtils {

    /**
     * 计算禁漫接口请求Headers的Token和TokenParam。
     *
     * @param ts     时间戳（秒）
     * @param ver    App版本（可选，默认为JmMagicConstants.APP_VERSION）
     * @param secret 密钥（可选，默认为JmMagicConstants.APP_TOKEN_SECRET）
     * @return 包含Token和TokenParam的字符串数组：{token, tokenparam}
     */
    public static String[] tokenAndTokenparam(long ts, String ver, String secret) {
        String finalVer = (ver != null) ? ver : JmMagicConstants.APP_VERSION;
        String finalSecret = (secret != null) ? secret : JmMagicConstants.APP_TOKEN_SECRET;

        // tokenparam: 1700566805,1.6.3
        String tokenparam = String.format("%d,%s", ts, finalVer);

        // token: 81498a20feea7fbb7149c637e49702e3
        String token = md5Hex(String.format("%d%s", ts, finalSecret));

        return new String[]{token, tokenparam};
    }

    /**
     * 解密禁漫接口的返回值数据。
     *
     * @param data   resp.json()['data']字段的Base64编码字符串
     * @param ts     时间戳（与生成Token时使用的相同）
     * @param secret 密钥（可选，默认为JmMagicConstants.APP_DATA_SECRET）
     * @return JSON格式的字符串
     * @throws Exception 如果解密失败
     */
    public static String decodeRespData(String data, Object ts, String secret) throws Exception {
        String finalSecret = (secret != null) ? secret : JmMagicConstants.APP_DATA_SECRET;

        // 1. Base64解码
        byte[] dataB64 = Base64.getDecoder().decode(data);

        // 2. 准备AES-ECB解密的密钥
        // 密钥是 (ts + secret) 的MD5哈希值
        String keyString = md5Hex(ts + finalSecret);
        byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");

        // 3. AES-ECB解密 (使用NoPadding模式，因为我们将手动处理padding)
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        byte[] dataAes = cipher.doFinal(dataB64);

        // 检查解密后的数据是否为空
        if (dataAes == null || dataAes.length == 0) {
            throw new RuntimeException("AES decryption resulted in empty data.");
        }


        // 4. 移除末尾的padding
        // 最后一个字节的值表示padding的长度
        int padding = dataAes[dataAes.length - 1];
        if (padding <= 0 || padding > dataAes.length) {
            throw new Exception("Invalid padding value: " + padding);
        }
        byte[] unpaddedData = Arrays.copyOf(dataAes, dataAes.length - padding);

        // 5. 解码为字符串 (json)
        return new String(unpaddedData, StandardCharsets.UTF_8);
    }

    /**
     * 解密禁漫接口的返回值数据，使用默认密钥。
     *
     * @param data resp.json()['data']字段的Base64编码字符串
     * @param ts   时间戳
     * @return JSON格式的字符串
     * @throws Exception 如果解密失败
     */
    public static String decodeRespData(String data, Object ts) throws Exception {
        return decodeRespData(data, ts, null);
    }

    /**
     * 计算字符串的MD5哈希值（十六进制表示）。
     *
     * @param key 待哈希的字符串
     * @return MD5哈希值的十六进制字符串
     * @throws AssertionError   如果key为null或非字符串
     * @throws RuntimeException 如果MD5算法不可用
     */
    public static String md5Hex(String key) {
        Asserts.requireTrue(key != null, "key参数需为字符串，不能为null。");
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(key.getBytes(StandardCharsets.UTF_8));

            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                // %02x 表示输出两位十六进制数，不足两位前面补0
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
    }

    // 私有构造函数，防止实例化
    private JmCryptoUtils() {
    }
}